package com.boxtelecom.chatConnection;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;

import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
import org.apache.harmony.javax.security.sasl.Sasl;
import org.apache.harmony.javax.security.sasl.SaslException;

import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Authentication;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;

import android.util.Log;

public class FacebookConnectSASLMechanism extends SASLMechanism {
	
    private String _apiKey = "";
    private String _sessionKey = "";
    private String _sessionSecret = "";
	
	public FacebookConnectSASLMechanism(SASLAuthentication arg0) {
		super(arg0);
		// TODO Auto-generated constructor stub
	}

	
	@Override
	protected void authenticate(){
		// TODO Auto-generated method stub
        StringBuilder stanza = new StringBuilder();
        stanza.append("<auth mechanism=\"").append(getName());
        stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
        stanza.append("</auth>");

        Packet packet2 = new AuthMechanism(getName(), null);
        
        // Send the authentication to the server
        getSASLAuthentication().send(packet2);
	}

	public void authenticate(String username, String host, CallbackHandler cbh)
			throws IOException, XMPPException {
		// TODO Auto-generated method stub
		Log.d("debug", "authenticate(string, string, callbackhandler)");
        String[] mechanisms = { "DIGEST-MD5" };
        Map<String, String> props = new HashMap<String, String>();
        sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
        authenticate();

	}

	@Override
	public void authenticate(String apiKeyAndSessionKey, String host, String sessionSecret) {
		// TODO Auto-generated method stub
	
		  if(apiKeyAndSessionKey==null || sessionSecret==null){
			  Log.d("error", "(sasl authenticate) Invalid parameters!");
		  }
      
	      String[] keyArray = apiKeyAndSessionKey.split("\\|");
	      
	      if(keyArray==null || keyArray.length != 2)
	              Log.d("error", "(sasl authenticate) Api key or session key is not present!");
	      
	      this._apiKey = keyArray[0];
	      this._sessionKey = keyArray[1];
	      this._sessionSecret = sessionSecret;
	      
	      this.authenticationId = _sessionKey;
	      this.password = sessionSecret;
	      this.hostname = host;
	   
	      String[] mechanisms = { "DIGEST-MD5" };
	      Map<String, String> props = new HashMap<String, String>();
	      try {
			sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
		} catch (SaslException e) {
			// TODO Auto-generated catch block
			Log.d("error", "(sasl authenticate) " + e.getMessage());
			Log.d("error", "(sasl authenticate) " + e.getLocalizedMessage());
		}
	
		authenticate();
	}

	@Override
	public void challengeReceived(String challenge) throws IOException {
		// TODO Auto-generated method stub
		StringBuilder stanza = new StringBuilder();

        byte response[] = null;
        if (challenge != null) {
                String decodedResponse = new String(Base64.decode(challenge));
                Map<String, String> parameters = getQueryMap(decodedResponse);

                String version = "1.0";
                String nonce = parameters.get("nonce");
                String method = parameters.get("method");
                
                Long callId = new GregorianCalendar().getTimeInMillis()/1000;
                
                String sig = "api_key=" + _apiKey
                           + "call_id=" + callId
                           + "method=" + method
                           + "nonce=" + nonce
                           + "session_key=" + _sessionKey
                           + "v=" + version
                           + _sessionSecret;
                
                try {
                        sig = MD5(sig);
                } catch (NoSuchAlgorithmException e) {
                        throw new IllegalStateException(e);
                }
                
                String composedResponse = "api_key=" + _apiKey + "&"
                                        + "call_id=" + callId + "&"
                                        + "method=" + method + "&"
                                        + "nonce=" + nonce + "&"
                                        + "session_key=" + _sessionKey + "&"
                                        + "v=" + version + "&"
                                        + "sig=" + sig;
                
                response = composedResponse.getBytes();
        }

        String authenticationText="";
        Packet packet = new Response();
        
        if (response == null) {
        	packet = new Response();
        } else {
            authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
            packet = new Response(authenticationText);
        }

        stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
        stanza.append(authenticationText);
        stanza.append("</response>");
               
        // Send the authentication to the server
        getSASLAuthentication().send(packet);
	}



	@Override
	protected SASLAuthentication getSASLAuthentication() {
		// TODO Auto-generated method stub
		return super.getSASLAuthentication();
	}

	@Override
	protected String getName() {
		// TODO Auto-generated method stub
		return "X-FACEBOOK-PLATFORM";
	}
	
    private Map<String, String> getQueryMap(String query) {
        String[] params = query.split("&");
        Map<String, String> map = new HashMap<String, String>();
        for (String param : params) {
                String name = param.split("=")[0];
                String value = param.split("=")[1];
                map.put(name, value);
        }
        return map;
    }
    
    private String convertToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < data.length; i++) {
            int halfbyte = (data[i] >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                if ((0 <= halfbyte) && (halfbyte <= 9))
                    buf.append((char) ('0' + halfbyte));
                else
                    buf.append((char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        }
        return buf.toString();
    }
	
    public String MD5(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException  {
        MessageDigest md;
        md = MessageDigest.getInstance("MD5");
        byte[] md5hash = new byte[32];
        md.update(text.getBytes("iso-8859-1"), 0, text.length());
        md5hash = md.digest();
        return convertToHex(md5hash);
    }

    public class AuthMechanism extends Packet{
    	final private String name; 
        final private String authenticationText;

        public AuthMechanism(String name, String authenticationText) {
            if (name == null) {
                throw new NullPointerException("SASL mechanism name shouldn't be null.");
            }
            this.name = name;
            this.authenticationText = authenticationText;
        }

        public String toXML() {
            StringBuilder stanza = new StringBuilder();
            stanza.append("<auth mechanism=\"").append(name);
            stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
            if (authenticationText != null &&
                    authenticationText.trim().length() > 0) {
                stanza.append(authenticationText);
            }
            stanza.append("</auth>");
            return stanza.toString();
        }
    }
}
